use strict;
use warnings;
use locale;
use utf8;
use Encode;

use URI::Escape;

my $msround=20;
 
my $file="";
my $outputfile="";
my $outputnum=0;
my $ret="";
my @caps;

# arguments flags:
my $ap=0;
my $at=0;
my $ac=0;
my $aj=0;
my $af=1;
my $au=1;
my $ad=0;
 
my $t; # special variable for (?{ }) regex

# UTF8 to ANSI conversion 
sub translateiso {
	my ($s) = @_;
    # strange bug if I put all in one tr
	$s =~ tr/♪/#/;
	$s =~ tr/≈/=/;
	$s =~ tr/ÀÂÉÈÊËÎÏÖÔÛÜÙŸ—/AAEEEEIIOOUUUY-/;  
	$s =~s/(?:« *| *»)/"/g;
	$s =~s/œ/oe/g;
	$s =~s/æ/ae/g;
	$s =~s/Æ/AE/g;
	$s =~s/Œ/OE/g;
	$s =~s/…/.../g;
	return $s;
}

sub fancy {
	my ($s) = @_;

	$s =~ tr/\r\t;/   /s;
	$s =~ s/\([^)]*?\)/ /g 	if ($ap);
	$s =~ s/\n(?!-)/ /sg	if ($aj);

	if ($au) {
		$s =~ s/["«»]([^"«»]*)["«»]/« $1 »/sg;
		$t = "";
		$s =~ s/([A-Z])(\.([A-Z])(?{$t=$t.$^N}))+\.?/$1$t/sg; # trick to merge acronyms B.B.D.O -> BBDO
		$s =~ s/\.\.\./…/sg;
		$s =~ s/--/—/sg; # long dash
		$s =~ tr/#~/♪≈/s; # music, equivalence ...
		$s =~ s/oe([ui])/œ$1/sg;		
	}

	if ($af) {
		$s =~ s/ *([?!:])/ $1/sg;
		$s =~ s/ *([.,…\]\)]) *(?![."\)0-9])/$1 /sg;
		$s =~ s/ *([\(\[]) */ $1/sg;
		$s =~ s/([?!,:.]) */$1 /sg;
		$s =~ s/^ *- */- /mg;
		$s =~ s/([.!?…][\n "«»()-]*)([a-zéèçàùâêûîôäëüïöæœ])/$1.uc($2)/sge;
	}
	$s =~ s/  +/ /sg;
	$s =~ s/(^ +| +$|^- *$)//mg;
	$s =~ s/(^\n+|\n+$)//s;

	$s =~ s/\n/\r\n/sg;
	
	return $s;
}

sub printTime {
	my ($t) = @_;
	my ($h, $m, $s, $ms);
	$s = int($t);
	$ms = int(($t - $s)*1000 + .5); #int( ($t - $s) * 50 + 0.5) * 20; # Timings are rounded to 1/50 sec multiples.
	$m = int($s / 60); $s = $s % 60;
	$h = int($m / 60); $m = $m % 60;
	return sprintf("%02d:%02d:%02d,%03d", $h, $m, $s, $ms);
}

my $ctr=0;
sub gogo {
	my ($rank, $from, $to, $s) = @_;
	my $fromF = 0.0;
	my $toF = 0.0;
	my $re = qr/(\d+)[:h ]+(\d+)[: ]+(\d+)[,. ]+(\d\d\d)/;
	if ($from =~ $re) {
		$fromF = (($1 *60) + $2) * 60 + $3 + $4 / 1000.0;
	}	
	if ($to =~ $re) {
		$toF = (($1 * 60) + $2) * 60 + $3 + $4 / 1000.0;
	}	
	
	push @caps, [ $rank, $fromF, $toF, fancy(decode('UTF-8', $s)) ] ;
	$ctr++;
	return "";
}



for (my $i = $#ARGV ; $i >= 0 ; $i--) {
  my $a = $ARGV[$i];
  if ($file eq "") {
	$file = $a;
	$outputfile = $file;
	my $num=0;
	if ($outputfile =~ /(^.*?)(?|\.+(\d+)\.+notag\.+(?:iso-*8859|utf-*8)\.+srt|\.+notag\.+(?:iso-*8859|utf-*8)\.+srt|\.+(\d+)\.+notag\.+srt|\.+notag\.+srt\.+(\d+)\.(?:iso-*8859|utf-*8)\.+srt|\.+(?:iso-*8859|utf-*8)\.+srt|\.+(\d+)\.+srt|\.+srt)$/i) {
		$outputnum = $2 if (defined $2);
		$outputnum++;
		$outputfile = "$1";
	}
  } 
  elsif ($a eq "-p") { # remove comments for unpaired
	$ap = ($ap + 1) % 2;
  }
  elsif ($a eq "-t") { # remove tags
	$at = ($at + 1) % 2;
  }
  elsif ($a eq "-c") { # CSV format for excel / Word, text only
	$ac = 1;
  }
  elsif ($a eq "-cc") { # CSV format for excel / Word, num + text
	$ac = 2;
  }
  elsif ($a eq "-ccc") { # CSV format for excel / Word, full format
	$ac = 3;
  }
  elsif ($a eq "-j") { # join lines to form wholes sentences
	$aj = ($aj + 1) % 2;
  }
  elsif ($a eq "-f") { # fix french typography
	$af = ($af + 1) % 2;
  }
  elsif ($a eq "-u") { # enhance quote (UTF8)
	$au = ($au + 1) % 2;
  }
  elsif ($a eq "-d") { # fix delay 1=>set min duration 
	$ad = 1;
  }
  elsif ($a eq "-dd") { # fix delay   2=> avoid overlaps
	$ad = 2;
  }
  elsif ($a eq "-ddd") { # fix delay 1=>set min duration + 2=> avoid overlaps
	$ad = 3;
  }
  else {
	print STDERR "Unknown argument : $a\n";
	die -1;
  }

}

if ( $file ne "") {	
	open(DAT, '<', $file) or die -3;
	binmode(DAT,":raw");
	read DAT, $ret, -s $file;
	close DAT;
}
else {
	print STDERR "subhelper [options...] file.srt\n".
	"with options:\n".
	"-p   : remove comments for unpaired persons\n".
	"-t   : remove tags\n".
	"-c   : export to CSV format, text only\n".
	"-cc  : export to CSV format, num + text\n".
	"-ccc : export to CSV format, full format\n".
	"-j   : join splitted lines to form a full sentence. Punctuation must be flawless.\n".
	"-f   : do not fix french punctuation\n".
	"-u   : do not fix french ligatures, double-quotes and ellipsis\n".
	"-d   : increase line duration according to text length\n".
	"-dd  : decrease line duration to avoid overlapping\n".
	"-ddd : do both of the above\n";
	die -2;
}

# parse entire file, fancy captions
$ctr=0;
$ret =~ s/(\d+)[\r\t ]*\n([0-9,.:h ]{12,13})[\r\t ]*-+>[\r\t ]*([0-9,.:h ]{12,13})[\r\t ]*\n(.*?)(?=\n[\r\t ]*\n|$)/gogo($1,$2,$3,$4)/sge;
print STDERR "total: $ctr \n";

# sort according to start time
@caps = sort { $a->[1] <=> $b->[1] } @caps; 
# (so now [0] is not the real index)

# join lines & uppercase first char of sentence
for (my $i = $#caps ; $i > 0 ; $i--) {
	my $sabove = $caps[$i - 1][3];
	my $s = $caps[$i][3];
	my $piece = "";
	if ($aj 
		&& (( $sabove =~ /[^.!?… "»]{2}[ "»]*$/ ) 
		|| $s =~ /^[- ]*(?:[—…]|\.\.\.)/ )) { # careful — (long dash) is not - (minus) 
		$s =~ s/^(?:[- …]*|\.\.\.)([^\n]*)\n?//s; 
		my $toto = $1;
		$sabove .= " ".$toto;
		$caps[$i - 1][3] = $sabove;
		$caps[$i][3] = $s;
		print STDERR "Joining ".$caps[$i][0]." to caption above\n";
	}
	elsif ($af && $sabove =~ /[.!?][ "»]*$/ ) { # put first letter of first caption in uppercase if a new sentence.
		$s =~ s/^([ "«»()-]*)([a-zéèçàùâêûîôäëüïöæœ])/$1.uc($2)/sge;
		$caps[$i][3] = $s;
	}
}

# fix delays 
if ($ad) {
	for (my $i = 1; $i < $#caps ; $i++) {
		# round millisec to 0.020 sec
		my $from = $caps[$i][1];
		my $to = $caps[$i][2];

		$from=int($from * 1000.0/$msround + 0.5) * $msround / 1000.0; 
		
		# count syllabes (in fact voyels and al.)
		my $count = () = $caps[$i][3] =~ /(?:[aeo]i|[ao]u|ao|[ou]e|a(?![uio])|o(?![ieu])|e(?!i|[snt]*\W)|u(?!e)|[iyàâéèêëïöôûüùÿÀÂÉÈÊËÎÏÖÔÛÜÙŸæœÆŒ1-9])/sig ;
		# supreme formula for french dialogues -- finely tuned so don't touch it.
		my $sec = 0.4 + $count * 0.135;
		# a caption followed by a big blank is credited for extra time
		if ($count 
			&& ($ad & 1) == 1 
			&& ($caps[$i + 1][1] - $to) > 2.5 
			&& ($to - $from) < 0.4 + $sec) {
				$to = $from + 0.4 + $sec;
				#print STDERR "Increasing ".$caps[$i][0]." up to ".sprintf('%1.3f', $to - $from)." sec as last caption in scene\n";
		}		
		# set duration according to syllabes if captions is too short, or if captions is already overlapping
		elsif ($count 
			&& ($ad & 1) == 1 
			&& (($to - $from) < $sec ||(($caps[$i + 1][1] - $to) < 1 / 23.9))) {
				$to = $from + $sec;
				#print STDERR "Setting ".$caps[$i][0]." up to ".sprintf('%1.3f',$to - $from)." sec\n";
		}
		# cut current caption to prevent overlaping to next caption. Add a gap of 1/23. seconds
		if ($count 
			&& ($ad & 2) == 2 
			&& ($caps[$i + 1][1] - $to) < 1 / 23.9) {
				$to = $caps[$i + 1][1] - 1 / 23.9;
				#print STDERR "Caping ".$caps[$i][0]." down to ".sprintf('%1.3f',$to - $from)." sec\n";
		}
		$caps[$i][1] = $from;
		$to = int($to * 1000.0/$msround + 0.5) * $msround / 1000.0;
		if ($to != $caps[$i][2]) {
			#print STDERR "Changing duration ".$caps[$i][0]." to ".sprintf('%+1.3f %.3f %.3f',($to-$from) - ($caps[$i][2]-$caps[$i][1]), $from, $to)." sec\n";
			$caps[$i][2] = $to;
		}
	}
}

# output files
if ($ac == 0) {
	for (my $autonum=$outputnum;;$autonum++) {
		if ( (! -e $outputfile.".".$autonum.".iso8859.srt") && (! -e $outputfile.".".$autonum.".srt") &&
			 (! -e $outputfile.".".$autonum.".notag.iso8859.srt") && (! -e $outputfile.".".$autonum.".notag.srt") &&
			open(ISO, '>', $outputfile.".".$autonum.".iso8859.srt") && open(UTF, '>', $outputfile.".".$autonum.".srt") &&
			open(ISONT, '>', $outputfile.".".$autonum.".notag.iso8859.srt") && open(UTFNT, '>', $outputfile.".".$autonum.".notag.srt")
			) {
			binmode(ISO, ":raw");
			binmode(UTF, ":raw");
			binmode(ISONT, ":raw");
			binmode(UTFNT, ":raw");
			for (my $i=0; $i <= $#caps ; $i++) {
				my $phrase = fancy($caps[$i][3]);
				my $deb="".($i+1)."\r\n".printTime($caps[$i][1])." --> ".printTime($caps[$i][2])."\r\n";				
				print UTF encode('UTF-8',$deb.$phrase."\r\n\r\n" );
				my $phrasent = $phrase; $phrasent =~ s/ *<\/?\w+> *//g;
				print UTFNT encode('UTF-8',$deb.$phrasent."\r\n\r\n" );
				$phrase=translateiso($phrase);
				print ISO encode('ISO-8859-1',$deb.$phrase."\r\n\r\n" );
				$phrasent=translateiso($phrasent);
				print ISONT encode('ISO-8859-1',$deb.$phrasent."\r\n\r\n" );
			}
			close(UTFNT);
			close(ISONT);
			close(UTF);
			close(ISO);
			print STDERR "4 files created: ".$outputfile.".".$autonum.'.*'."\n";
			last;
		}
	}
}
else {
	for (my $autonum=$outputnum;;$autonum++) {
		if ( (! -e $outputfile.".".$autonum.".csv") && open(UTF, '>', $outputfile.".".$autonum.".csv")) {
			binmode(UTF, ":raw");
			for (my $i=0; $i <= $#caps ; $i++) {
				my $phrase = fancy($caps[$i][3]);
				$phrase =~ s/ *<\/?\w+> *//g	if ($at);

				if ($ac == 1) { # CSV (semicolon separated fields)
					print UTF encode('UTF-8', $phrase."\n");
				}
				elsif ($ac == 2) { # CSV
					my $s = $phrase;
					$s =~ s/^/;/mg;
					print UTF encode('UTF-8', ($i+1).$s."\n");
				}
				elsif ($ac == 3) { # CSV
					$phrase =~ s/^/;;;/mg;
					my $s = ($i + 1).';'.$caps[$i][1].';'.$caps[$i][2];
					$phrase =~ s/^;;/$s/s;
					print UTF encode('UTF-8', $phrase."\n");
				}
			}
			close(UTF);
			print STDERR "CSV file created: ".$outputfile.".".$autonum.".csv \n";
			last;
		}
	}
}